package com.chukun.gateway.core.filter.chain;

import com.chukun.gateway.common.constants.GlobalFilterConst;
import com.chukun.gateway.common.rule.Rule;
import com.chukun.gateway.core.context.GatewayContext;
import com.chukun.gateway.core.filter.Filter;
import com.chukun.gateway.core.filter.annotation.FilterDefinition;
import com.chukun.gateway.core.filter.factory.FilterFactory;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author chukun
 * @version 1.0.0
 * @description 过滤器工厂实现类
 * @createTime 2023/12/3 22:14
 */
@Slf4j
public class GatewayFilterChainFactory implements FilterFactory {

    private Map<String, Filter> processorFilters = new ConcurrentHashMap<>();

    // 规则过滤器缓存
    private Cache<String, GatewayFilterChain> chainCache = Caffeine.newBuilder()
            .recordStats()
            .expireAfterWrite(10L, TimeUnit.MINUTES)
            .build();


    private GatewayFilterChainFactory() {

        ServiceLoader<Filter> serviceLoader = ServiceLoader.load(Filter.class);
        serviceLoader.stream().forEach(filterProvider -> {
            Filter filter = filterProvider.get();
            FilterDefinition annotation = filter.getClass().getAnnotation(FilterDefinition.class);
            if (annotation != null) {
                log.info("load filter success:{},{},{},{}", filter.getClass(), annotation.id(), annotation.name(), annotation.order());
                String filterId = annotation.id();
                if (StringUtils.isBlank(filterId)) {
                    filterId = filter.getClass().getName();
                }
                if (!processorFilters.containsKey(filterId)) {
                    processorFilters.put(filterId, filter);
                }
            }
        });

    }

    private static class SingletonInstance{
        private static final GatewayFilterChainFactory INSTANCE = new GatewayFilterChainFactory();
    }

    public static GatewayFilterChainFactory getInstance(){
        return SingletonInstance.INSTANCE;
    }

    @Override
    public GatewayFilterChain buildFilterChain(GatewayContext ctx) throws Exception {
        return chainCache.get(ctx.getRule().getId(),k->doBuildFilterChain(ctx.getRule()));
    }

    public GatewayFilterChain doBuildFilterChain(Rule rule) {
        GatewayFilterChain chain = new GatewayFilterChain();
        List<Filter> filters = new ArrayList<>();
        filters.add(getFilterInfo(GlobalFilterConst.GRAY_FILTER_ID));
        filters.add(getFilterInfo(GlobalFilterConst.MONITOR_FILTER_ID));
        filters.add(getFilterInfo(GlobalFilterConst.MONITOR_END_FILTER_ID));
        if(rule != null){
            Set<Rule.FilterConfig> filterConfigs =   rule.getFilterConfigs();
            Iterator<Rule.FilterConfig> iterator = filterConfigs.iterator();
            Rule.FilterConfig filterConfig;
            while(iterator.hasNext()){
                filterConfig = iterator.next();
                if(filterConfig == null){
                    continue;
                }
                String filterId = filterConfig.getId();
                if(StringUtils.isNotEmpty(filterId) && getFilterInfo(filterId) != null){
                    Filter filter = getFilterInfo(filterId);
                    filters.add(filter);
                }
            }
        }
        filters.add(getFilterInfo(GlobalFilterConst.ROUTER_FILTER_ID));
        //排序
        filters.sort(Comparator.comparingInt(Filter::getOrder));
        //添加到链表中
        chain.addFilterList(filters);
        return chain;
    }

    @Override
    public Filter getFilterInfo(String filterId) {
        return processorFilters.get(filterId);
    }
}
